#include "Pilha.hpp"
#include "Aplicacao.hpp"
#include <iostream>
#include <cstdio>


//Pilha::Pilha(std::string config) {
Pilha::Pilha() {

	std::cout << "Initing" << std::endl;
    aplicacao = new Aplicacao();
    transporte = new Transporte();
    rede = new Rede();
    enlace = new Enlace(13);
    fisico = new Fisico();

    aplicacao->setDownLayer(transporte);
    transporte->setDownLayer(rede);
    rede->setDownLayer(enlace);
    enlace->setDownLayer(fisico);

    transporte->setUpLayer(aplicacao);
    rede->setUpLayer(transporte);
    enlace->setUpLayer(rede);
    fisico->setUpLayer(enlace);

    aplicacao->init();
    transporte->init();
    rede->init();
    enlace->init();
    fisico->init();
    
}

Pilha::~Pilha() {
    
	/*delete this->aplicacao;
	delete this->transporte;
	delete this->rede;
	delete this->enlace;
	delete this->fisico;*/
}

void Pilha::escreve (int macDestino, int tamanho, unsigned char* tostr) {

	std::cout << "Escreve" << std::endl;

	int mask = 0x80;//Mascará para pegar o bit da posição para criar Bits (1000 0000)2
	for (int i = 0; i < tamanho; i++) {

		Bits* bs = new Bits(8);
		/*for (int j = 0; j < 8; j++) {

			nb->set(0, dados[i] & mask);
			mask >>= 1;
		}*/

		std::cout << "Sending " << tostr[i] << std::endl;
		mask = 0x80;
                bs->set(i*8, tostr[i] & mask);
                mask = mask >> 1;
                bs->set(i*8+1, tostr[i] & mask);
                mask = mask >> 1;
                bs->set(i*8+2, tostr[i] & mask);
                mask = mask >> 1;
                bs->set(i*8+3, tostr[i] & mask);
                mask = mask >> 1;
                bs->set(i*8+4, tostr[i] & mask);
                mask = mask >> 1;
                bs->set(i*8+5, tostr[i] & mask);
                mask = mask >> 1;
                bs->set(i*8+6, tostr[i] & mask);
                mask = mask >> 1;
                bs->set(i*8+7, tostr[i] & mask);

		bs->println();
		this->aplicacao->send(bs);
	}
	std::cout << "End escreve" << std::endl;
}

unsigned int Pilha::le (unsigned char* leb, unsigned int tam) {

	static int tamBuffer = 0;
	static unsigned char* buffer = 0;

	//Verifica se o número de bytes para ler está no buffer
	if (tam <= tamBuffer) {

		memcpy(leb, buffer, tam);
		return tam;
	}

	unsigned int byteFill = 0;
	//Verifica se há algo no buffer e adiciona
	if (tamBuffer > 0) {

		memcpy(leb, buffer, tamBuffer);
		//Deleta o buffer
		delete buffer;
		buffer = 0;
		tamBuffer = 0;
		byteFill = tamBuffer;
	}

	//Vamos lendo até encher o tam
	while (byteFill < tam) {

		Bits* r = this->aplicacao->le();
		
		unsigned char* baux = 0;
		unsigned int tr = r->toCharArray(&baux);
		std::cout << "tbr" << std::endl;
		r->println();
		//Lemos mais bytes. Temos que colocar no buffer
		if (tr + byteFill > tam) {

			unsigned int diff = tr - byteFill;
			/**************************************************************************************************************************/
			//                             ____      _       ____      _       ____     _    
			//			                U | __")uU  /"\  uU /"___|uU  /"\  uU /"___|U  /"\  u
			//			                 \|  _ \/ \/ _ \/ \| |  _ / \/ _ \/ \| | u   \/ _ \/
		    //                            | |_) | / ___ \  | |_| |  / ___ \  | |/__  / ___ \
			//			                  |____/ /_/   \_\  \____| /_/   \_\  \____|/_/   \_\
			//			      _           || \\_  \\    >>  _)(|_   \\    >> _// \\  \\    >>
			//			                 (__) (__)(__)  (__)(__)__) (__)  (__)__)(__)(__)  (__)
			/***************************************************************************************************************************/
                        unsigned char* aux = buffer;
                        unsigned char* buffer = new unsigned char[tamBuffer + diff];
                        memcpy(buffer, aux, tamBuffer);
                        memcpy(buffer, baux + diff, byteFill);
                        delete aux;
                        tamBuffer = tamBuffer + diff;
		} else { //Só copiamos

			std::cout << "ahhaah " << tr << std::endl;
			printf("%p\n", leb);
			memcpy(leb, baux, tr);
			std::cout << baux[0] << std::endl;
			std::cout << "dada" << std::endl;
			byteFill += tr;
		}
		
		//Estamos no topo da conexão podemos liberar a memória
		//delete baux;
		//delete r;
	}

	return tam;
}

void Pilha::close(void) {

	//Enviar de algum modo uma mensagem de fim de conexão
	//A camada de transporte que deveria fazer isso
}
